//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-02-22     ZhangYihua   first version
//
// Description  : 
//################################################################################

//
//          n               i                                       0
//          ------  >>> --->+------  >>> ----------------------------
//          ^               ^                                       | 
//          |  ___          |  ___              ___         ___     |
//          | |   |         | |   |            |   |       |   |    |
// PRBS_DW  --|PDn|<- ... <---|PDi|<-- ...  <--|PD2|<------|PD1|<----  feedback
//            |___|           |___|            |___|       |___|
//                                               |           |   
//                                               |           |
//                                               V           V
// OUT_DW                              ....    out1        out0   (tx MSB first in serial mode)

// M sequence list
// n  ini  G(x)
// 3  -     3, 2   => G(x) = x^3 + x^2 + 1
// 7  -     7, 6   => G(x) = x^7 + x^6 + 1
// 9  -     9, 5
// 11 one  11, 9
// 15 zero 15, 14
// 19 -    19, 6,  2,  1
// 20 one  20, 17 (20,  3)
// 23 zero 23, 18
// 31 -    31, 28
// 33 -    33, 20
// 39 -    39, 35
// 41 -    41, 38
// 47 -    47, 42
// 63 -    63, 62

// need to define PRBS_DW as localparameter before line `include "func_prbs.v"
// PRBS_DW = MAX_F(PRBS_N, OUT_DW)
function [PRBS_DW-1:0] prbs_nxt_f;
    input   [PRBS_DW-1:0]   prbs_cur;
    input   integer         PRBS_N;     // must PRBS_DW >= PRBS_N, select G(x) according to PRBS_N
    input   integer         SHIFT_N;    // must PRBS_DW >= OUT_DW >= SHIFT_N, bit iteration times equals to SHIFT_N 
    input                   PRBS_INI;   // 1'b0: inverse feedback, because initial of prbs_cur is all zeros; 
                                        // 1'b1: non-inverse feedback, because initial of prbs_cur is all ones; 
    input                   MSB_FIRST;  // 1'b1: tx bit[OUT_DW-1] first in serial mode; else tx bit[0] first

    integer                 i;
    reg     [PRBS_DW-1:0]   dt;
    reg     [0:0]           dr;
    reg                     fb;
    begin
        if (MSB_FIRST==1'b1) begin
            dt = prbs_cur;
        end else begin
            for (i=0; i<PRBS_DW; i=i+1) begin
                 dt[i] = prbs_cur[PRBS_DW-1-i];
            end
        end

        repeat(SHIFT_N) begin
            case(PRBS_N)
                3  : dr = (dt>>(3 -1)) ^ (dt>>(2 -1));
                7  : dr = (dt>>(7 -1)) ^ (dt>>(6 -1));
                9  : dr = (dt>>(9 -1)) ^ (dt>>(5 -1));
                10 : dr = (dt>>(10-1)) ^ (dt>>(7 -1));
                11 : dr = (dt>>(11-1)) ^ (dt>>(9 -1));
                15 : dr = (dt>>(15-1)) ^ (dt>>(14-1));
                19 : dr = (dt>>(19-1)) ^ (dt>>(6 -1)) ^ (dt>>(2-1)) ^ (dt>>(1-1));
                20 : dr = (dt>>(20-1)) ^ (dt>>(17-1));
                23 : dr = (dt>>(23-1)) ^ (dt>>(18-1));
                31 : dr = (dt>>(31-1)) ^ (dt>>(28-1));
                33 : dr = (dt>>(33-1)) ^ (dt>>(20-1));
                39 : dr = (dt>>(39-1)) ^ (dt>>(35-1));
                41 : dr = (dt>>(41-1)) ^ (dt>>(38-1));
                47 : dr = (dt>>(47-1)) ^ (dt>>(42-1));
                63 : dr = (dt>>(63-1)) ^ (dt>>(62-1));
            endcase

            if (PRBS_INI==1'b0)
                fb = ~dr[0];
            else
                fb =  dr[0];

            dt = {dt[PRBS_DW-2:0], fb};
        end

        if (MSB_FIRST==1'b1) begin
            prbs_nxt_f = dt;
        end else begin
            for (i=0; i<PRBS_DW; i=i+1) begin
                 prbs_nxt_f[i] = dt[PRBS_DW-1-i];
            end
        end
    end
endfunction

